<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
    <title>BHF JSON Support</title>
</head>
<body bgcolor="white">
<p>
    The Blue Hole family of libraries provides a high performance implementation, Blue Hole JSON (BHJ) marshalling and unmarshalling of Java
    objects to and from JSON. Blue Hole components use client side state that is passed back and forth between the client
    and the services running on the server. This state transfer stresses both the network and the server, which
    must spend a fair portion of its cycles writing and parsing this state. The more common JSON support
    libraries use reflection for an easy implementation with runtime flexibility at the expense of performance. With
    our need for high performance we have chosen another design center by generating, at runtime, marshalling and
    unmarshalling classes for the specific state classes. The performance is essentially that of hand coded
    serialization code without the use of reflection. In return for high performance, we give up flexibility in the
    sense that the state structure must be concrete, not abstract, and fixed for a given class. This constraint
    also allows us to use true Java objects and POJOs rather than pseudo objects such as JSONObject and JSONArray
    that are needed for other JSON libraries.
</p>
<p>
    To be specific, your state objects (services, components, event arguments) subject to JSON marshalling or
    unmarshalling must follow the following rules.
</p>
<ul>
    <li>The object graph must not be cyclic.</li>
    <li>Inner classes are not allowed unless they are public and static.</li>
    <li>Getters must be of the form getXYZ() or, in the case of a boolean property, isXYZ(). Indexed properties
        are not supported: use arrays or lists instead.</li>
    <li>Simple propeties must be of a primitive type, primitive wrapper (Integer, ...), Enum, or java.util.Date.</li>
    <li>Complex properties may be an array, parameterized List, or pameterized Map. Note that Lists
        and Maps <b>MUST</b> be parameterized with concrete types. The parameters provide the needed type information
        for generating the writing and parsing classes.</li>
    <li>Maps are represented as JSON objects where the key is used as the name, and the entry value as the
        object value. String conversion is used to transform the key into a quoted form.</li>
    <li>Dates are represented as a single value, ms since midnight 1970, compatible with both the Java and
        JavaScript notion of Date. To distinguish Dats from Strings, Dates are enclosed in '@' signs. i.e.
        "@...@"</li>
    <li>Properties may be nested as needed.</li>
    <li>All properties must have both setters and getters.</li>
    <li>All objects must have a public default constructor.</li>
    <li>All types must be concrete, except for Lists and Maps <b>IF</b> they are pre-instantiated so that
        the parsing code does not have to figure out how to instantiate an abstract class or interface.</li>
</ul>
<p>
    For code generation, the BHJ library uses the JBOSS javassist package. This package supports a subset of the Java
    1.5 language. We expect that there are still edge cases that may cause the class generation to fail. Start with a
    small object you know to work and build in additional complexity while testing at each iteration. This approach will
    make it easier to determine the cause of JSON problems as it is difficult to trace a run time fault back to a
    specific property causing the trouble.
</p>
<p>
    Note that you can use the JSONConfig annotation to shield properties from marshalling. Use this annotation
    with the getter method of the property to hide.
</p>
<p>
    To use the BHJ JSON library, you will need to include bhf-1.0.0.jar and javassist.jar: the rest is easy...
    <code><pre>
    public void testDoc() throws IOException
    {
        AddressBook x       = new AddressBook();
        AddressBook y;
        JSONWriter  writer  = new JSONWriter();
        JSONParser  parser  = new JSONParser();
        String      json;

        x.setOwner( new Person( "Mary", 41, Sex.FEMALE ) );
        x.getPeople().add( new Person( "Mary", 41, Sex.FEMALE ) );
        x.getPeople().add( new Person( "Bob", 14, Sex.MALE ) );
        x.getPeople().add( new Person( "Alice", 22, Sex.FEMALE ) );
        x.getPeople().add( new Person( "Greg", 64, Sex.MALE ) );
        x.getPeople().add( new Person( "Sharon", 9, Sex.FEMALE ) );
        x.getPeople().add( null );

        // Convert object to JSON
        json = writer.writeObject( x );
        // Convert JSON back to an equivalent object.
        y = parser.parseObject( new AddressBook(), json );
    }
    </pre></code>
</p>
<p>
    You can use the <code>@JSONTransient</code> on a getter to skip that property when writing JSON. For more complex
    situations, you can use methods on <code>JSONWriter</code> to specify one or more properties that should be
    skipped.
    <code><pre>
        final JSONWriter resultWriter = new JSONWriter();
        ...
        resultWriter.excludeProperties( User.class, new String[] {
            "language",
            "timeZone",
            "roleMap",
            "info",
        } );
        ...
        resultWriter.writerObject( user );
    </pre></code>
</p>
<p>
    You cannot parse JSON directly into a generic type, such as a collection. For example, the following will NOT
    work because one cannot figure out the generic type of an object from an instance, since this information is
    lost at runtime.
    <code><pre>
        public List&lt;Map&gt; readMapFile( final String resource )
            throws IOException
        {
            final String        json    = readToString( resource );
            final JSONParser    parser  = new JSONParser();
            final List&lt;Map&gt;     maps    = new ArrayList&lt;Map&gt;();

            return parser.parseObject( maps, json );
        }
    </pre></code>

    The solution is to use a helper class to provide the correct Type for the object to be parsed.

    <code><pre>
        public List&lt;Map&gt; readMapFile( final String resource )
            throws IOException, NoSuchMethodException
        {
            final String        json    = readToString( resource );
            final JSONParser    parser  = new JSONParser();
            final List&lt;Map&gt;     maps    = new ArrayList&lt;Map&gt;();

            return parser.parseObject( Typer.class.getMethod( "maps" ).getGenericReturnType(), maps, json );
        }

        final static class Typer
        {
            public List&lt;Map&gt; maps() { return null; }
        }
    </pre></code>
</p>
@see org.bhf.json.JSONParser#parseObject(Type,Object,String)
@see org.bhf.json.JSONParser#parseObject(Type,Object,String)
@see org.bhf.json.JSONWriter
@see org.bhf.json.JSONTransient
</body>
</html>